
#include <BFont>
#include <BStyle>
#include <BOpenGL>

#include "IAlignItem.h"
#include "member_BStyle.h"
#include "member_BObject.h"

using namespace BWE;

IAlignItem::IAlignItem(BObject* object)
{
	this->object = object;
	align = Align_LeftCenter;
	form = Form_None;
	spacing = 1;
	graph = Graph_None;
	widget = 0;
	border = 0;
	lineWidth = 1;
	lineFeed = false;
	maxSize.set(999, 999);

	dirty = object;
	dirtyStyle = object;
	translatable = true;
}
IAlignItem::IAlignItem(const IAlignItem& other)
{
	object = other.object;
	align = other.align;
	form = other.form;
	spacing = other.spacing;
	text = other.text;
	color = other.color;
	graph = other.graph;
	image = other.image;
	icon = other.icon;
	widget = other.widget;
	iconSize = other.iconSize;
	lineWidth = other.lineWidth;
	lineFeed = other.lineFeed;
	tips = other.tips;

	baseSize = other.baseSize;
	minSize = other.minSize;
	maxSize = other.maxSize;
	border = other.border;
	margin = other.margin;

	dirty = true;
	dirtyStyle = true;
	translatable = true;
}
IAlignItem::~IAlignItem()
{

}

BRect IAlignItem::rect() const
{
	return BRect(pos, size);
}

void IAlignItem::setPos(int x, int y)
{
	pos.set(x, y);
	if (widget)
	{
		adjustWidget();
	}
}
void IAlignItem::setPos(const BPoint& pos)
{
	if (this->pos != pos)
	{
		this->pos = pos;
		if (widget)
		{
			adjustWidget();
		}
		if (object)
			object_member(object)->check_emit(Signal_Moved, pos);
	}
}

void IAlignItem::setWidth(int width)
{
	if (size.width() != width)
	{
		setSize(BSize(width, size.height()));
	}
}
void IAlignItem::setHeight(int height)
{
	if (size.height() != height)
	{
		setSize(BSize(size.width(), height));
	}
}
void IAlignItem::setSize(int width, int height)
{
	setSize(BSize(width, height));
}
void IAlignItem::setSize(const BSize& size)
{
	BSize temp = size;
	temp.limit(minSize, maxSize);
	if (this->size != temp)
	{
		this->size = temp;
		freshRects();
		if (object)
			object_member(object)->check_emit(Signal_Resized, temp);
	}
}

void IAlignItem::dispenseStyles(const BStyle* style)
{
	if (realStyle)
	{
		realStyle->clearAll();
		realStyle->clearAnnexes();
		realStyle->clearChildren();
	}
	if (const BStyle* itemStyle = style->annex("item"))
	{
		if (!realStyle)
			realStyle = new BStyle();
		style_member(realStyle)->overlap(itemStyle);
	}
	style_member(style)->apply(this);
}
void IAlignItem::translateText(const BStyle* style)
{

}

void IAlignItem::applyStyle(const BStyle* style)
{
	if (realStyle)
		style = realStyle;

	if (style_member(style)->assign(Value_Color, color) && object)
		object_member(object)->check_emit(Signal_ColorChanged, color);

	if (style_member(style)->assign(Value_Graph, graph) && object)
		object_member(object)->check_emit(Signal_GraphChanged, graph);

	BImage* image = style->value(Value_Image);
	if (image && this->image != image)
	{
		this->image = image;
		if (object)
			object_member(object)->check_emit(Signal_ImageChanged, image);
	}

	BIcon* icon = style->value(Value_Icon);
	if (icon && this->icon != icon)
	{
		this->icon = icon;
		if (object)
			object_member(object)->check_emit(Signal_IconChanged, icon);
	}

	style_member(style)->assign(Value_Width, baseSize.width());
	style_member(style)->assign(Value_Height, baseSize.height());

	style_member(style)->assign(Value_Min_Width, minSize.width());
	style_member(style)->assign(Value_Max_Width, maxSize.width());

	style_member(style)->assign(Value_Min_Height, minSize.height());
	style_member(style)->assign(Value_Max_Height, maxSize.height());

	style_member(style)->assign(Value_Translatable, translatable);

	if (style_member(style)->assign(Value_Text, text) && object)
		object_member(object)->check_emit(Signal_TextChanged, text);

	if (style_member(style)->assign(Value_Tips, tips) && object)
		object_member(object)->check_emit(Signal_TipsChanged, tips);

	if (style_member(style)->assign(Value_Form, form) && object)
		object_member(object)->check_emit(Signal_FormatChanged, form);

	if (style_member(style)->assign(Value_Align, align) && object)
		object_member(object)->check_emit(Signal_AlignChanged, align);

	if (style_member(style)->assign(Value_Spacing, spacing) && object)
		object_member(object)->check_emit(Signal_SpacingChanged, spacing);

	if (style_member(style)->assign(Value_Border, border) && object)
		object_member(object)->check_emit(Signal_BorderChanged, border);

	style_member(style)->assign(Value_Margin_Top, margin.top());
	style_member(style)->assign(Value_Margin_Left, margin.left());
	style_member(style)->assign(Value_Margin_Right, margin.right());
	style_member(style)->assign(Value_Margin_Bottom, margin.bottom());

	style_member(style)->assign(Value_Icon_Size, iconSize);
	style_member(style)->assign(Value_Line_Width, lineWidth);
	style_member(style)->assign(Value_Line_Height, block.lineSize);
	style_member(style)->assign(Value_Line_Spacing, block.lineSpacing);

	block.dirty = true;
	block.setStyle(style);

	BSize radiusLeftTop;
	BSize radiusRightTop;
	BSize radiusLeftBottom;
	BSize radiusRightBottom;
	if (round)
	{
		radiusLeftTop = round->radiusLeftTop;
		radiusRightTop = round->radiusRightTop;
		radiusLeftBottom = round->radiusLeftBottom;
		radiusRightBottom = round->radiusRightBottom;
	}
	style_member(style)->assign(Value_Radius_Left_Top, radiusLeftTop);
	style_member(style)->assign(Value_Radius_Right_Top, radiusRightTop);
	style_member(style)->assign(Value_Radius_Left_Bottom, radiusLeftBottom);
	style_member(style)->assign(Value_Radius_Right_Bottom, radiusRightBottom);

	if (radiusLeftTop.empty() && radiusRightTop.empty() && radiusLeftBottom.empty() && radiusRightBottom.empty())
	{
		if (round)
		{
			round = 0;
			dirty = true;
		}
	}
	else
	{
		if (round.empty())
			round = new IRound();
		round->radiusLeftTop = radiusLeftTop;
		round->radiusRightTop = radiusRightTop;
		round->radiusLeftBottom = radiusLeftBottom;
		round->radiusRightBottom = radiusRightBottom;
		dirty = true;
	}

	dirty = true;
}
void IAlignItem::freshSize(const BFont* font, int width)
{
	shellSize.width() = border + margin.left() + margin.right() + border;
	shellSize.height() = border + margin.top() + margin.bottom() + border;
	width -= shellSize.width();
	pos.reset();
	size.set(width, 0);
	ia_form = form;
	ia_align = align;
	ia_spacing = spacing;

	block.text.reset();
	if (realStyle)
	{
		if (const BFont* realFont = realStyle->value(Value_Font))
			font = realFont;
		if (translatable)
			realStyle->translate(text, block.text);
	}
	if (block.text.empty() && translatable)
		translateText(realStyle);
	if (block.text.empty())
		block.text = text;
	block.font = font;
	block.reset();
	if (lineFeed)
		block.adjust(width);
	else
		block.adjust(0);
	ia_textSize = block.size();

	ia_iconSize.reset();
	bool hasImage = icon || image || graph || widget;
	bool showImage = hasImage || (ia_form >= Form_IconOnly && ia_form <= Form_IconUnderText);
	if (ia_form == Form_IconAlone && !hasImage)
		showImage = false;
	if (ia_form == Form_TextAlone && block.text.empty() && hasImage)
		showImage = true;
	if (showImage)
	{
		ia_iconSize = iconSize;
		if (widget && widget->visible())
		{
			BSize sizeHint = widget->sizeHint();
			sizeHint = widget->sizeLimit(sizeHint);
			if (ia_iconSize.width() < 1)
				ia_iconSize.width() = sizeHint.width();
			if (ia_iconSize.height() < 1)
				ia_iconSize.height() = sizeHint.height();
		}
		if (font)
		{
			if (ia_iconSize.width() < 1)
				ia_iconSize.width() = font->size();
			if (ia_iconSize.height() < 1)
				ia_iconSize.height() = font->size();
		}
	}
	this->adjust(BRect());
	hintSize = ia_hintSize + shellSize;
	if (hintSize.width() < baseSize.width())
		hintSize.width() = baseSize.width();
	if (hintSize.height() < baseSize.height())
		hintSize.height() = baseSize.height();
	hintSize.limit(minSize, maxSize);
}
void IAlignItem::freshRects()
{
	float b = (float)border;
	float width = (float)size.width();
	float height = (float)size.height();

	float mleft = (float)margin.left();
	float mright = (float)margin.right();
	float mtop = (float)margin.top();
	float mbottom = (float)margin.bottom();

	float cx = b + mleft;
	float cy = b + mtop;
	float cw = width - b - mleft - mright - b;
	float ch = height - b - mtop - mbottom - b;

	rectTop.set(cx, b, cw, mtop);
	rectLeft.set(b, cy, mleft, ch);
	rectRight.set(width - b - mright, cy, mright, ch);
	rectBottom.set(cx, height - b - mbottom, cw, mbottom);
	rectLeftTop.set(b, b, mleft, mtop);
	rectLeftBottom.set(b, height - b - mbottom, mleft, mbottom);
	rectRightTop.set(width - b - mright, b, mright, mtop);
	rectRightBottom.set(width - b - mright, height - b - mbottom, mright, mbottom);
	rectCenter.set(cx, cy, cw, ch);

	if (round)
	{
		round->frameSize.set((float)width, (float)height);
		round->centerLeftTop.set(round->radiusLeftTop.width(), round->radiusLeftTop.height());
		round->centerRightTop.set(width - round->radiusRightTop.width(), round->radiusRightTop.height());
		round->centerLeftBottom.set(round->radiusLeftBottom.width(), height - round->radiusLeftBottom.height());
		round->centerRightBottom.set(width - round->radiusRightBottom.width(), height - round->radiusRightBottom.height());

		BPointfArray outerPoints;
		BPointfArray innerPoints;

		BRectf outerRect(0, 0, width, height);
		round->createPoints(outerRect, outerPoints);

		BRectf innerRect = outerRect;
		innerRect.left() += border;
		innerRect.right() -= border;
		innerRect.top() += border;
		innerRect.bottom() -= border;

		round->createPoints(innerRect, innerPoints);
		round->createTriangles(outerPoints, innerPoints, round->borderPoints);

		round->backgroundPoints = innerPoints;
		round->backgroundPoints.prepend(rectCenter.center());

		innerRect.left() = (float)rectLeft.right();
		innerRect.top() = (float)rectTop.bottom();
		innerRect.right() = (float)rectRight.left();
		innerRect.bottom() = (float)rectBottom.top();

		outerPoints.reset();
		outerPoints.append(innerPoints);

		round->createPoints(rectCenter, innerPoints);
		round->createTriangles(outerPoints, innerPoints, round->marginPoints);

		round->centerPoints = innerPoints;
		round->centerPoints.prepend(rectCenter.center());
	}
	block.dirty = true;
	if (lineFeed)
		block.adjust((int)rectCenter.width());
	else
		block.adjust(0);
	ia_textSize = block.size();
	adjust(rectCenter);
	block.setPos(ia_textRect.pos());
	if (widget)
	{
		adjustWidget();
	}
}
void IAlignItem::adjustWidget()
{
	BRect rect = ia_iconRect;
	if (widget->widthPolicy() & Policy_Expanding && rect.width() < rectCenter.width())
	{
		rect.left() = (int)rectCenter.left();
		rect.right() = (int)rectCenter.right();
	}
	if (widget->heightPolicy() & Policy_Expanding && rect.height() < rectCenter.height())
	{
		rect.top() = (int)rectCenter.top();
		rect.bottom() = (int)rectCenter.bottom();
	}
	widget->setRect(rect + pos);
	if (widget->size() != rect.size())
	{
		BRect realRect = rect.align(ia_align, widget->size());
		widget->setRect(realRect);
	}
}

void IAlignItem::drawBackground(BPainter& painter, State state) const
{
	BRectf backRect(0, size);
	backRect.left() += border;
	backRect.right() -= border;
	backRect.top() += border;
	backRect.bottom() -= border;

	if (const BImage* image = painter.image(Image_Background, state))
	{
		painter.drawImage(backRect, image, margin);
		return;
	}

	if (painter.setColor(background) || painter.setColor(Color_Background, state))
	{
		if (round)
		{
			glEnable(GL_MULTISAMPLE);
			glBegin(GL_TRIANGLE_FAN);
			for (int i = 0; i < round->backgroundPoints.size(); i++)
			{
				const BPointf& p = round->backgroundPoints[i];
				glVertex2f(p.x(), p.y());
			}
			glEnd();
			glDisable(GL_MULTISAMPLE);
		}
		else
		{
			painter.fillRect(backRect);
		}
	}
}
void IAlignItem::drawMargin(BPainter& painter, State state) const
{
	if (margin.empty())
		return;

	const BRectf& crect = rectCenter;
	float w = (float)size.width();
	float h = (float)size.height();

	float left = (float)margin.left();
	float top = (float)margin.top();
	float right = (float)margin.right();
	float bottom = (float)margin.bottom();

	if (const BImage* image = painter.image(Image_Margin, state))
	{
		painter.bind(image);
		int tw = image->width();
		int th = image->height();
		painter.fillRect(rectLeft, BRectf(0, top, left, th - top - bottom));
		painter.fillRect(rectTop, BRectf(left, 0, tw - left - right, top));
		painter.fillRect(rectRight, BRectf(tw - right, top, right, th - top - bottom));
		painter.fillRect(rectBottom, BRectf(left, th - bottom, tw - left - right, bottom));
		painter.fillRect(rectLeftTop, BRectf(0, 0, left, top));
		painter.fillRect(rectRightTop, BRectf(tw - right, 0, right, top));
		painter.fillRect(rectLeftBottom, BRectf(0, th - bottom, left, bottom));
		painter.fillRect(rectRightBottom, BRectf(tw - right, th - bottom, right, bottom));
		painter.unbind();
		return;
	}

	if (painter.setColor(Color_Margin, state))
	{
		if (round)
		{
			glEnable(GL_MULTISAMPLE);
			painter.fillTriangles(round->marginPoints);
			glDisable(GL_MULTISAMPLE);
		}
		else
		{
			painter.fillRect(rectTop);
			painter.fillRect(rectLeft);
			painter.fillRect(rectRight);
			painter.fillRect(rectBottom);

			painter.fillRect(rectLeftTop);
			painter.fillRect(rectRightTop);
			painter.fillRect(rectLeftBottom);
			painter.fillRect(rectRightBottom);
		}
	}
}
void IAlignItem::drawBorder(BPainter& painter, State state) const
{
	if (border < 1)
		return;

	float b = (float)border;
	float w = (float)size.width();
	float h = (float)size.height();

	if (const BImage* image = painter.image(Image_Border, state))
	{
		painter.bind(image);
		int tw = image->width();
		int th = image->height();
		painter.fillRect(rectLeft, BRectf(0, b, b, th - b - b));
		painter.fillRect(rectTop, BRectf(b, 0, tw - b - b, b));
		painter.fillRect(rectRight, BRectf(tw - b, b, b, th - b - b));
		painter.fillRect(rectBottom, BRectf(b, th - b, tw - b - b, b));
		painter.fillRect(rectLeftTop, BRectf(0, 0, b, b));
		painter.fillRect(rectRightTop, BRectf(tw - b, 0, b, b));
		painter.fillRect(rectLeftBottom, BRectf(0, th - b, b, b));
		painter.fillRect(rectRightBottom, BRectf(tw - b, th - b, b, b));
		painter.unbind();
		return;
	}
	if (painter.setColor(Color_Border, state))
	{
		if (round)
		{
			glEnable(GL_MULTISAMPLE);
			painter.fillTriangles(round->borderPoints);
			glDisable(GL_MULTISAMPLE);
		}
		else
		{
			painter.fillRect(float(0), b, b, h - b - b);
			painter.fillRect(float(0), float(0), w, b);
			painter.fillRect(w - b, b, b, h - b - b);
			painter.fillRect(float(0), h - b, w, b);
		}
	}
}
void IAlignItem::drawContent(BPainter& painter, State state) const
{
	if (ia_iconRect.valid())
	{
		BImage* image = this->image;
		if (icon)
		{
			image = icon->image(state);
		}
		if (!painter.setColor(color))
		{
			painter.setColor(Color_Text, state);
		}
		if (image)
		{
			if (BGlyph* glyph = dynamic_cast<BGlyph*>(image))
				glyph->setSize(ia_iconRect.size());
			painter.drawImage(ia_iconRect, image, true);
		}
		else
		{
			painter.setLineSmooth(true);
			painter.setLineWidth(lineWidth);
			if (graph)
			{
				painter.drawGraph(ia_iconRect, graph);
			}
			if (graph <= Graph_Round_Fill && (state & State_Checked))
			{
				painter.setColor(Color_Arrow, state);
				BRectf iconRect = ia_iconRect % 56;
				BPointf p0 = iconRect.leftCenter();
				BPointf p1 = iconRect.bottomCenter();
				BPointf p2 = iconRect.rightTop();
				painter.drawLine(p0, p1);
				painter.drawLine(p1, p2);
			}
		}
	}
	if (painter.setColor(Color_Text, state))
	{
		block.paint(painter);
	}
}
